#include <stdarg.h>
#include <stdio.h>
#include <errno.h>
#include "aos/kernel.h"
#include <k_api.h>
#include "aos/hal/spi.h"
#include "fsl_clock.h"
#include "fsl_lpspi.h"
#include "board.h"

#define SPI_CLOCK_SOURCE_SELECT  (1U)
#define SPI_CLOCK_SOURCE_DIVIDER (7U)
#define SPI_CLOCK_FREQ           (CLOCK_GetFreq(kCLOCK_Usb1PllPfd0Clk) / (SPI_CLOCK_SOURCE_DIVIDER + 1U))

#define TRANSFER_BAUDRATE (50000U)

#define SPI_3_CONFIG_FLAG kLPSPI_MasterPcs0

static lpspi_master_config_t g_spi_config[4] = {
{
    .baudRate     = TRANSFER_BAUDRATE,
    .bitsPerFrame = 8,
    .cpol         = kLPSPI_ClockPolarityActiveHigh,
    .cpha         = kLPSPI_ClockPhaseFirstEdge,
    .direction    = kLPSPI_MsbFirst,

    .pcsToSckDelayInNanoSec        = 1000000000 / TRANSFER_BAUDRATE,
    .lastSckToPcsDelayInNanoSec    = 1000000000 / TRANSFER_BAUDRATE,
    .betweenTransferDelayInNanoSec = 1000000000 / TRANSFER_BAUDRATE,

    .whichPcs           = kLPSPI_Pcs0,
    .pcsActiveHighOrLow = kLPSPI_PcsActiveLow,
    .pinCfg             = kLPSPI_SdiInSdoOut,
    .dataOutConfig      = kLpspiDataOutRetained,
},
{
    .baudRate     = TRANSFER_BAUDRATE,
    .bitsPerFrame = 8,
    .cpol         = kLPSPI_ClockPolarityActiveHigh,
    .cpha         = kLPSPI_ClockPhaseFirstEdge,
    .direction    = kLPSPI_MsbFirst,

    .pcsToSckDelayInNanoSec        = 1000000000 / TRANSFER_BAUDRATE,
    .lastSckToPcsDelayInNanoSec    = 1000000000 / TRANSFER_BAUDRATE,
    .betweenTransferDelayInNanoSec = 1000000000 / TRANSFER_BAUDRATE,

    .whichPcs           = kLPSPI_Pcs0,
    .pcsActiveHighOrLow = kLPSPI_PcsActiveLow,
    .pinCfg             = kLPSPI_SdiInSdoOut,
    .dataOutConfig      = kLpspiDataOutRetained,
},

{
    .baudRate     = TRANSFER_BAUDRATE,
    .bitsPerFrame = 24,//24,
    .cpol         = kLPSPI_ClockPolarityActiveLow,
    .cpha         = kLPSPI_ClockPhaseSecondEdge,
    .direction    = kLPSPI_MsbFirst,

    .pcsToSckDelayInNanoSec        = 1000000000 / TRANSFER_BAUDRATE,
    .lastSckToPcsDelayInNanoSec    = 1000000000 / TRANSFER_BAUDRATE,
    .betweenTransferDelayInNanoSec = 1000000000 / TRANSFER_BAUDRATE,

    .whichPcs           = kLPSPI_Pcs0,
    .pcsActiveHighOrLow = kLPSPI_PcsActiveLow,
    .pinCfg             = kLPSPI_SdiInSdoOut,
    .dataOutConfig      = kLpspiDataOutRetained,
},

{
    .baudRate     = TRANSFER_BAUDRATE,
    .bitsPerFrame = 8,
    .cpol         = kLPSPI_ClockPolarityActiveHigh,
    .cpha         = kLPSPI_ClockPhaseFirstEdge,
    .direction    = kLPSPI_MsbFirst,

    .pcsToSckDelayInNanoSec        = 1000000000 / TRANSFER_BAUDRATE,
    .lastSckToPcsDelayInNanoSec    = 1000000000 / TRANSFER_BAUDRATE,
    .betweenTransferDelayInNanoSec = 1000000000 / TRANSFER_BAUDRATE,

    .whichPcs           = kLPSPI_Pcs0,
    .pcsActiveHighOrLow = kLPSPI_PcsActiveLow,
    .pinCfg             = kLPSPI_SdiInSdoOut,
    .dataOutConfig      = kLpspiDataOutRetained,
}};

int32_t get_spi_base(uint32_t port, LPSPI_Type **base)
{
    if (base == NULL) {
        return -1;
    }
    switch(port) {
        case 1:
            *base = (LPSPI_Type*)LPSPI1_BASE;
            break;
        case 2:
            *base = (LPSPI_Type*)LPSPI2_BASE;
            break;
        case 3:
            *base = (LPSPI_Type*)LPSPI3_BASE;
            break;
        case 4:
            *base = (LPSPI_Type*)LPSPI4_BASE;
            break;
        default:
            return -1;

    }

    return 0;
}

int32_t get_spi_config_flag(uint32_t port, uint32_t *flag)
{
    if (flag == NULL) {
        return -1;
    }

    switch(port) {
        case 3:
            *flag = SPI_3_CONFIG_FLAG;
            break;
        default:
            return -1;

    }

    return 0;
}

int32_t hal_spi_init(spi_dev_t *spi)
{
    uint32_t clock;
    LPSPI_Type* base;
    lpspi_master_config_t* config;
    status_t status;

    if (spi == NULL){
        return -1;
    }

    switch(spi->port) {
        case 3:
            base = (LPSPI_Type*)LPSPI3_BASE;
            config = &g_spi_config[spi->port-1];
            clock = SPI_CLOCK_FREQ;
            break;
        default:
            break;

    }


    LPSPI_MasterInit(base, config, clock);

    return 0;
}

int32_t hal_spi_send(spi_dev_t *spi, const uint8_t *data, uint16_t size, uint32_t timeout)
{
    int32_t          ret;
    status_t         stat;
    LPSPI_Type      *base;
    uint32_t         flag;
    lpspi_transfer_t transfer;

    if (spi == NULL){
        return -1;
    }

    if (data == NULL){
        return -1;
    }

    if (size == 0){
        return -1;
    }

    if (timeout == 0)
    {
        timeout = 1000;
    }

    ret = get_spi_base(spi->port, &base);
    if (ret != 0)
    {
        return -1;
    }

    ret = get_spi_config_flag(spi->port, &flag);
    if (ret != 0)
    {
        return -1;
    }

    transfer.txData = (uint8_t *)&data[0];
    transfer.rxData = NULL;
    transfer.dataSize = size;
    transfer.configFlags = flag;

    stat = LPSPI_MasterTransferBlocking(base, &transfer);
    if (stat != kStatus_Success) {
        return -1;
    }

    return 0;
}

int32_t hal_spi_send_recv(spi_dev_t * spi, uint8_t * tx_data, uint8_t * rx_data, uint16_t size, uint32_t timeout)
{
    int32_t          ret;
    status_t         stat;
    LPSPI_Type      *base;
    uint32_t         flag;
    lpspi_transfer_t transfer;

    if (spi == NULL) {
        return -1;
    }

    if ((tx_data == NULL) || (rx_data == NULL)) {
        return -1;
    }

    if (size == 0) {
        return -1;
    }

    if (timeout == 0) {
        timeout = 1000;
    }

    ret = get_spi_base(spi->port, &base);
    if (ret != 0)
    {
        return -1;
    }

    ret = get_spi_config_flag(spi->port, &flag);
    if (ret != 0)
    {
        return -1;
    }

    transfer.txData = (uint8_t *)&tx_data[0];
    transfer.rxData = (uint8_t *)&rx_data[0];
    transfer.dataSize = size;
    transfer.configFlags = flag;

    stat = LPSPI_MasterTransferBlocking(base, &transfer);
    if (stat != kStatus_Success) {
        return -1;
    }

    return 0;
}

int32_t hal_spi_finalize(spi_dev_t *spi)
{
    int32_t          ret;
    LPSPI_Type      *base;

    if (spi == NULL) {
        return -1;
    }

    ret = get_spi_base(spi->port, &base);
    if (ret != 0)
    {
        return -1;
    }

    LPSPI_Deinit(base);

    return 0;
}
